#include "ge_image.hpp"
#include "png_chuncks.hpp"
#include <stdexcept>
//#include <FreeImage.h>

/*#ifdef _MSC_VER
//#ifdef _DEBUG
//#pragma comment(lib, "FreeImaged.lib")
//#else
#pragma comment(lib, "FreeImage.lib")
//#endif
#endif*/

using namespace std;

namespace ge {
  image::image()
  : _width(0),
    _height(0),
    _pixels() { }
  // Implementation using FreeImage
  /*istream & operator>>(istream & in, image & img) {
    
  }
  
  // Read input file for Freeimage
  void load(string const & file_name) {
    FIBITMAP * const image_original = FreeImage_Load(FIF_PNG, file_name.c_str());
    if(!image_original)
      throw runtime_error(("Could not load file " + file_name).c_str());
      
    FIBITMAP * const image_24_bits = FreeImage_ConvertTo24Bits(image_original);
    _width = FreeImage_GetWidth(image_24_bits);
    _height = FreeImage_GetHeight(image_24_bits);
    
    BYTE * const image_bits = FreeImage_GetBits(image_24_bits);
    _pixels.clear();
    _pixels.resize(_width * _height * 3);
   
    for(int y = 0; y < _height; ++y)
      for(int x = 0; x < _width; ++x) {
        RGBQUAD color;
        FreeImage_GetPixelColor(image_24_bits, x, y, &color);

        _pixels[3 * (x + _width * y) + 0] = color.rgbRed;
        _pixels[3 * (x + _width * y) + 1] = color.rgbGreen;
        _pixels[3 * (x + _width * y) + 2] = color.rgbBlue;
      }
      
    FreeImage_Unload(image_24_bits);
    FreeImage_Unload(image_original); 
  }*/
  
  istream & operator>>(istream & in, image & img) {
    int const c_size = 1024;
    char c[c_size] = {0};
    
    in.read(c, 8);
    if(memcmp(c, png::signature, 8))
      throw runtime_error("Unsupported file format."); // Only supporting PNG at this time.
    
    in.read(c, 8);
    if(memcmp(PNG_CHUNCK_NAME(c), "IHDR", 4))
      throw runtime_error("Corrupted PNG file: No IHDR chunk found.");
    
    in.read(c, PNG_CHUNCK_LENGTH(c));
    
    img._width = PNG_IHDR_WIDTH(c);
    img._height = PNG_IHDR_HEIGHT(c);
    
    short const bit_depth = PNG_IHDR_BIT_DEPTH(c),
                color_type = PNG_IHDR_COLOR_TYPE(c);
    // Check if bit depth is compatible with color type.
    // see: Table 11.1 in http://www.w3.org/TR/2003/REC-PNG-20031110/
    switch(color_type) {
      case png::CT_GREYSCALE:
        switch(bit_depth) {
          case 1:
          case 2:
          case 4:
          case 8:
          case 16:
            break;
          default:
            throw runtime_error("Corrupted PNG file: Invalid bit depth with color type greyscale.");
        }
        break;
      case png::CT_TRUECOLOR:
        switch(bit_depth) {
          case 8:
          case 16:
            break;
          default:
            throw runtime_error("Corrupted PNG file: Invalid bit depth with color type truecolor.");
        }
        break;
      case png::CT_INDEXED_COLOR:
        switch(bit_depth) {
          case 1:
          case 2:
          case 4:
          case 8:
            //if (PNG_CHUNCK_LENGTH(c)
            break;
          default:
            throw runtime_error("Corrupted PNG file: Invalid bit depth with color type indexed-color.");
        }
        break;
      case png::CT_GREYSCALE_WITH_ALPHA:
        switch(bit_depth) {
          case 8:
          case 16:
            break;
          default:
            throw runtime_error("Corrupted PNG file: Invalid bit depth with color type greyscale with alpha.");
        }
        break;
      case png::CT_TRUECOLOR_WITH_ALPHA:
        switch(bit_depth) {
          case 8:
          case 16:
            break;
          default:
            throw runtime_error("Corrupted PNG file: Invalid bit depth with color type truecolor with alpha.");
        }
        break;
    }
    
    if(PNG_IHDR_COMPRESSION_METHOD(c) != png::CM_DEFLATE_INFLATE)
      throw runtime_error("Corrupted PNG file: Unsupported compression method.");
    
    if(PNG_IHDR_FILTER_METHOD(c) != png::FM_ADAPTIVE)
      throw runtime_error("Corrupted PNG file: Unsupported filter method.");
      
    short const interlace = PNG_IHDR_INTERLACE_METHOD(c);
    switch(interlace) {
      case png::IM_NONE:
      case png::IM_ADAM7:
        break;
      default:
        throw runtime_error("Corrupted PNG file: Unsupported interlace method.");
    }

    // Read CRC
    in.read(c, 4);

    in.read(c, 8);
    if(memcmp(PNG_CHUNCK_NAME(c), "IDAT", 4))
      throw runtime_error("Corrupted PNG file: No IDAT chunk found.");

    img._pixels.resize(img._width*img._height);

    png::u4bytes idat_length = PNG_CHUNCK_LENGTH(c);
    cout << idat_length <<endl;
    while(idat_length > 0) {
      in.read(c, c_size);
      for(int i = 0; i < img._width; ++i)
        for(int j = 0; j < img._height; ++j) {
          img._pixels[i + j * img._width] = c[i + j * img._width];
        }
      idat_length -= c_size;
    }
    return in;
  }
}
